﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SpiceNet.Diagnostics;

namespace SpiceNet.Circuits
{
    /// <summary>
    /// A class that can track circuit nodes
    /// </summary>
    public class NodeList
    {

        /// <summary>
        /// Gets a list of nodes
        /// </summary>
        private List<CircuitNode> nodes;
        private Dictionary<string, CircuitNode> nodemap;

        /// <summary>
        /// Get the ground node
        /// </summary>
        public CircuitNode Ground { get; private set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public NodeList()
        {
            // Get a list of nodes
            nodes = new List<CircuitNode>();
            nodemap = new Dictionary<string, CircuitNode>();

            // Create the ground node
            Ground = new CircuitNode(NodeType.Voltage);
            Ground.Name = "GND";
            nodemap.Add("GND", Ground);
        }

        /// <summary>
        /// Find the row that matches a node name
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public CircuitNode this[string node]
        {
            get
            {
                if (nodemap.ContainsKey(node))
                    return nodemap[node];
                return null;
            }
        }

        /// <summary>
        /// Find the node at index
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public CircuitNode this[int index]
        {
            get
            {
                return nodes[index];
            }
        }

        /// <summary>
        /// Get the number of nodes
        /// </summary>
        public int Count { get { return nodes.Count; } }

        /// <summary>
        /// Map a node in the circuit
        /// </summary>
        /// <param name="name">The name of the node or null for extra equations</param>
        /// <returns></returns>
        public CircuitNode Map(string name, NodeType type = NodeType.Voltage)
        {
            // Check for ground
            if (!string.IsNullOrWhiteSpace(name))
            {
                if (nodemap.ContainsKey(name))
                    return nodemap[name];
            }

            // Not found, must be a new one
            CircuitNode mynode = new CircuitNode(type, nodes.Count + 1);
            mynode.Name = name;
            nodes.Add(mynode);
            if (!string.IsNullOrEmpty(name))
                nodemap.Add(name, mynode);
            return mynode;
        }

        /// <summary>
        /// Check if a node exists with this name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool ContainsNode(string name)
        {
            if (nodemap.ContainsKey(name))
                return true;
            return false;
        }

        /// <summary>
        /// Create an alias for a certain node
        /// </summary>
        /// <param name="name"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        public CircuitNode Alias(string name, string existing)
        {
            if (!nodemap.ContainsKey(existing))
                throw new CircuitException("Cannot alias node names to a non-existing node " + existing);
            if (nodemap.ContainsKey(name))
                throw new CircuitException("Name is already used in the circuit. Cannot alias " + name + " with " + existing);
            nodemap.Add(name, nodemap[existing]);
            return nodemap[existing];
        }

        /// <summary>
        /// Clear any node information
        /// </summary>
        public void Clear()
        {
            nodes.Clear();
            nodemap.Clear();
            nodemap.Add("GND", Ground);
        }
    }
}
